#!/usr/bin/env python3
"""
CodeQL扫描流水线调度器
支持定时调度和手动触发
"""

import os
import sys
import time
import signal
import logging
from pathlib import Path
from datetime import datetime
from typing import Optional
import schedule
import yaml
from codeql_pipeline import CodeQLPipeline


class CodeQLScheduler:
    """CodeQL扫描流水线调度器"""
    
    def __init__(self, config_path: str = None):
        """初始化调度器"""
        self.config_path = config_path or Path(__file__).parent / 'config.yaml'
        self.config = self._load_config()
        self.pipeline = None
        self.running = False
        self.setup_logging()
        self.setup_signal_handlers()
    
    def _load_config(self) -> dict:
        """加载配置"""
        if not Path(self.config_path).exists():
            raise FileNotFoundError(f"配置文件does not exist: {self.config_path}")
        
        with open(self.config_path, 'r', encoding='utf-8') as f:
            return yaml.safe_load(f)
    
    def setup_logging(self):
        """设置日志"""
        log_dir = Path(self.config['output']['results_dir']) / 'logs'
        log_dir.mkdir(parents=True, exist_ok=True)
        
        log_file = log_dir / 'scheduler.log'
        
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler(log_file, encoding='utf-8'),
                logging.StreamHandler(sys.stdout)
            ]
        )
        self.logger = logging.getLogger(__name__)
    
    def setup_signal_handlers(self):
        """设置信号Processing器"""
        def signal_handler(signum, frame):
            self.logger.info(f"收到信号 {signum}，正在停止调度器...")
            self.running = False
        
        signal.signal(signal.SIGINT, signal_handler)
        signal.signal(signal.SIGTERM, signal_handler)
    
    def run_scan(self, languages: list = None):
        """执行扫描任务"""
        try:
            self.logger.info("开始执行定时扫描任务")
            start_time = datetime.now()
            
            # 创建流水线实例
            self.pipeline = CodeQLPipeline(self.config_path)
            
            # 执行扫描
            self.pipeline.run_pipeline(languages)
            
            end_time = datetime.now()
            duration = end_time - start_time
            
            self.logger.info(f"扫描任务完成，耗时: {duration}")
            
            # 发送通知（如果配置了）
            self._send_notification(True, duration)
            
        except Exception as e:
            self.logger.error(f"扫描任务执行failed: {e}")
            self._send_notification(False, None, str(e))
    
    def _send_notification(self, success: bool, duration=None, error: str = None):
        """发送通知"""
        if not self.config.get('notifications', {}).get('email_enabled', False):
            return
        
        try:
            # 这里可以添加邮件发送逻辑
            # 由于需要SMTP配置，暂时只记录日志
            if success:
                self.logger.info(f"Scan successful完成，耗时: {duration}")
            else:
                self.logger.error(f"Scan failed: {error}")
        except Exception as e:
            self.logger.error(f"发送通知failed: {e}")
    
    def start_scheduler(self):
        """Start scheduler"""
        if not self.config.get('scheduler', {}).get('enabled', False):
            self.logger.warning("调度器未启用，请检查配置文件")
            return
        
        cron_expression = self.config['scheduler'].get('cron_expression', '0 2 * * *')
        timezone = self.config['scheduler'].get('timezone', 'Asia/Shanghai')
        
        self.logger.info(f"Start scheduler，调度时间: {cron_expression}，时区: {timezone}")
        
        # 解析cron表达式并设置调度
        self._parse_cron_and_schedule(cron_expression)
        
        self.running = True
        while self.running:
            try:
                schedule.run_pending()
                time.sleep(60)  # 每分钟检查一次
            except KeyboardInterrupt:
                self.logger.info("收到中断信号，停止调度器")
                break
            except Exception as e:
                self.logger.error(f"调度器运行异常: {e}")
                time.sleep(60)
        
        self.logger.info("调度器已停止")
    
    def _parse_cron_and_schedule(self, cron_expression: str):
        """解析cron表达式并设置调度"""
        parts = cron_expression.split()
        if len(parts) != 5:
            raise ValueError(f"无效的cron表达式: {cron_expression}")
        
        minute, hour, day, month, weekday = parts
        
        # 简化的cron解析，支持常见的调度模式
        if minute == '0' and hour != '*':
            # 每小时执行
            if hour.isdigit():
                schedule.every().day.at(f"{hour}:00").do(self.run_scan)
            else:
                schedule.every().hour.do(self.run_scan)
        elif minute != '*' and hour == '*':
            # 每分钟执行
            if minute.isdigit():
                schedule.every().minute.at(f":{minute}").do(self.run_scan)
            else:
                schedule.every().minute.do(self.run_scan)
        elif minute == '0' and hour == '2' and day == '*' and month == '*' and weekday == '*':
            # 每天凌晨2点执行
            schedule.every().day.at("02:00").do(self.run_scan)
        elif minute == '0' and hour == '0' and day == '*' and month == '*' and weekday == '0':
            # 每周日午夜执行
            schedule.every().sunday.at("00:00").do(self.run_scan)
        else:
            # 默认每天凌晨2点执行
            self.logger.warning(f"不支持的cron表达式: {cron_expression}，使用默认调度（每天凌晨2点）")
            schedule.every().day.at("02:00").do(self.run_scan)
    
    def run_once(self, languages: list = None):
        """Execute one scan immediately"""
        self.logger.info("执行一次性扫描任务")
        self.run_scan(languages)
    
    def list_scheduled_jobs(self):
        """列出已调度的任务"""
        jobs = schedule.get_jobs()
        if not jobs:
            print("没有已调度的任务")
        else:
            print("已调度的任务:")
            for job in jobs:
                print(f"  - {job}")


def main():
    """Main function"""
    import argparse
    
    parser = argparse.ArgumentParser(description='CodeQL扫描流水线调度器')
    parser.add_argument('--config', help='配置文件路径')
    parser.add_argument('--languages', nargs='+', help='Specify languages to scan')
    parser.add_argument('--run-once', action='store_true', help='Execute one scan immediately')
    parser.add_argument('--list-jobs', action='store_true', help='列出已调度的任务')
    parser.add_argument('--start-scheduler', action='store_true', help='Start scheduler')
    
    args = parser.parse_args()
    
    try:
        scheduler = CodeQLScheduler(args.config)
        
        if args.list_jobs:
            scheduler.list_scheduled_jobs()
        elif args.run_once:
            scheduler.run_once(args.languages)
        elif args.start_scheduler:
            scheduler.start_scheduler()
        else:
            # 默认Start scheduler
            scheduler.start_scheduler()
            
    except Exception as e:
        print(f"调度器启动failed: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()
